// @coding: utf8

#include <iostream>
#include <cstdint>
#include <vector>
#include <algorithm>
#include <cassert>
#include <ctime>
#include <random>
#include <chrono>

/**
 * 两种实现：
 * 1.每个牌型独立判定
 * 2.一个函数完成全部类型判定
 * *3.查表法，这个就不实现了
 */

int ALL_CARDS[] = {
    0x102,0x103,0x104,0x105,0x106,0x107,0x108,0x109,0x10a,0x10b,0x10c,0x10d,0x10e, //分别代表方块2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x202,0x203,0x204,0x205,0x206,0x207,0x208,0x209,0x20a,0x20b,0x20c,0x20d,0x20e, //分别代表梅花2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x302,0x303,0x304,0x305,0x306,0x307,0x308,0x309,0x30a,0x30b,0x30c,0x30d,0x30e, //分别代表红桃2,3,4,5,6,7,8,9,10,J,Q,K,A
    0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40a,0x40b,0x40c,0x40d,0x40e, //分别代表黑桃2,3,4,5,6,7,8,9,10,J,Q,K,A]
};

// 花色
enum ECardColor : int
{
    kHeart = 1,
    kSpade = 2,
    kClub = 3,
    kDiamond = 4,
};

// 牌型
enum EPokerHandType : int
{
    kInvalid = 0,  // 无效类型
    kRoyalFlush = 1,  // 皇家同花顺
    kStraigthFlush = 2, // 同花顺
    kFourOfAKind = 3, // 金刚
    kFullHouse = 4,  // 葫芦
    kFlush = 5,  // 同花
    kStraigth = 6,  // 顺子
    kThreeOfAKind = 7,  // 三条
    kTwoPair = 8,  // 两队
    kOnePair = 9,  // 一对
    kHighCard = 10,  // 高牌
};

// @brief 获取指定花色
inline int get_color(int card)
{
    return card >> 8;
}

// @brief 获取牌值
inline int get_value(int card)
{
    return card & 0x00ff;
}

// @brief 比较两张扑克:  left < right
inline bool compare_card_lesser(int left, int right)
{
    auto left_value = get_value(left);
    auto right_value = get_value(right);
    if (left_value < right_value)
        return true;
    else
    {
        if (left_value == right_value)
        {
            if (get_color(left) < get_color(right))
                return true;
        }

    }
    return false;
}

// @brief 牌型分析辅助结构
class CardsAnalazyInfo
{
public:
    CardsAnalazyInfo() {}

    void clear()
    {
        _is_straigth = false;
        _is_royal_flush = false;
        _is_flush = false;
        _two_cnt = 0;
        _three_cnt = 0;
        _four_cnt = 0;
        _cards.clear();
    }

    void analaze(const std::vector<int>& cards)
    {
        if (cards.size() != 5)
            return;
        clear();
        _cards = cards;
        std::sort(_cards.begin(), _cards.end(), compare_card_lesser);
        int line_cnt = 1;  // 记录值连续张数
        int same_flush_cnt = 1;
        int same_value_cnt = 1;
        for (std::size_t idx = 1; idx < _cards.size(); ++idx)
        {
            // 牌值连续检查
            if (get_value(_cards[idx-1]) + 1 == get_value(_cards[idx]))
                line_cnt++;
            // 花色相同检查
            if (get_color(_cards[idx-1]) == get_color(_cards[idx]))
                same_flush_cnt++;
            // 牌值相同检查
            if (get_value(_cards[idx-1]) == get_value(_cards[idx]))
                same_value_cnt++;
            else
            {
                switch (same_value_cnt)
                {
                case 2:
                    _two_cnt++;
                    break;
                case 3:
                    _three_cnt++;
                    break;
                case 4:
                    _four_cnt++;
                    break;
                }
                same_value_cnt = 1;
            }
        }
        // 最后一次same_value统计
        switch (same_value_cnt)
        {
        case 2:
            _two_cnt++;
            break;
        case 3:
            _three_cnt++;
            break;
        case 4:
            _four_cnt++;
            break;
        }
        // 同花判定
        if (5 == same_flush_cnt)
            _is_flush = true;
        // 顺子和皇家同花顺判定
        if (5 == line_cnt)
            _is_straigth = true;
        else if (4 == line_cnt)
        {
            // A2345也是顺子，如果花色一样为皇家同花顺
            if (get_value(_cards[4]) == 0x0e && get_value(_cards[0]) == 0x02)
            {
                _is_straigth = true;
            }
        }
        // 皇家同花顺
        if (_is_flush)
        {
            if (get_value(_cards[0]) == 0x0a)
                _is_royal_flush = true;
        }
    }

    int get_hand_card_type()
    {
        if (_is_royal_flush)
            return EPokerHandType::kRoyalFlush;
        if (_is_straigth && _is_flush)
            return EPokerHandType::kStraigthFlush;
        if (_four_cnt)
            return EPokerHandType::kFourOfAKind;
        if (_three_cnt && _two_cnt)
            return EPokerHandType::kFullHouse;
        if (_is_flush)
            return EPokerHandType::kFlush;
        if (_is_straigth)
            return EPokerHandType::kStraigth;
        if (_three_cnt)
            return EPokerHandType::kThreeOfAKind;
        if (_two_cnt == 2)
            return EPokerHandType::kTwoPair;
        if (_two_cnt == 1)
            return EPokerHandType::kOnePair;
        return EPokerHandType::kHighCard;
    }

protected:
    bool _is_straigth = false;                  // 是否顺子
    bool _is_royal_flush = false;               // 是否皇家同花顺
    bool _is_flush = false;                     // 是否同花
    int _two_cnt = 0;                           // 两张一样的数量
    int _three_cnt = 0;                         // 三张一样的数量
    int _four_cnt = 0;                          // 四张一样的数量
    std::vector<int> _cards;                    // 排序好的原始牌组
};

// @brief 牌型判定
int get_hand_card_type_v2(const std::vector<int>& input)
{
    if (input.size() != 5)
        return EPokerHandType::kInvalid;
    CardsAnalazyInfo analazy_info;
    analazy_info.analaze(input);
    return analazy_info.get_hand_card_type();
}

// @brief 同花顺
bool is_royal_flush(const std::vector<int>& input)
{
    if (input.size() < 5)
        return false;
    // 皇家同花顺一定从10开始
    if (get_value(input[0]) != 0x0a)
        return false;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_color(input[idx-1]) != get_color(input[idx]))
            return false;
        if (get_value(input[idx-1]) + 1 != get_value(input[idx]))
            return false;
    }
    return true;
}

// @brief 顺子
bool is_straigth_flush(const std::vector<int>& input)
{
    if (input.size() < 5)
        return false;
    int continue_cnt = 1;   // 最大连续数量
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_color(input[idx-1]) != get_color(input[idx]))
            break;
        if (get_value(input[idx-1]) + 1 != get_value(input[idx]))
            break;
        continue_cnt++;
    }
    // 末尾如果是A，检查顺子起点是否2
    if (4 == continue_cnt)
    {
        if (0x0e == get_value(input[4]) && 0x02 == get_value(input[0]) && get_color(input[0]) == get_color(input[4]))
            continue_cnt += 1;
    }
    return continue_cnt == 5;
}

// @brief 金刚
bool is_four_of_a_kind(const std::vector<int>& input)
{
    int max_same_cnt = 0;
    int same_cnt = 1;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_value(input[idx-1]) == get_value(input[idx]))
            same_cnt++;
        else
        {
            if (same_cnt > max_same_cnt)
                max_same_cnt = same_cnt;
            same_cnt = 1;
        }
    }
    if (same_cnt != 1)
        max_same_cnt = same_cnt > max_same_cnt ? same_cnt : max_same_cnt;
    return max_same_cnt == 4;
}

// @brief 葫芦
bool is_full_house(const std::vector<int>& input)
{
    int same_cnt = 1;
    int two_cnt = 0;  // 两张一样的次数
    int three_cnt = 0;  // 三张一样的次数
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_value(input[idx-1]) == get_value(input[idx]))
            same_cnt++;
        else
        {
            if (same_cnt == 3)
                three_cnt += 1;
            else if (same_cnt == 2)
                two_cnt += 1;
            same_cnt = 1;
        }
    }
    if (same_cnt != 1)
    {
        if (same_cnt == 3)
            three_cnt += 1;
        else if (same_cnt == 2)
            two_cnt += 1;
    }
    return three_cnt && two_cnt;
}

// @brief 同花
bool is_flush(const std::vector<int>& input)
{
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_color(input[idx - 1]) != get_color(input[idx]))
            return false;
    }
    return true;
}

// @brief 顺子
bool is_straight(const std::vector<int>& input)
{
    int continue_cnt = 1;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
         if (get_value(input[idx - 1]) + 1 != get_value(input[idx]))
            break;
        continue_cnt++;
    }
    if (4 == continue_cnt)
    {
        if(0x0e == get_value(input[4]) && 0x02 == get_value(input[0]))
            continue_cnt++;
    }
    return continue_cnt == 5;
}

// @brief 三条
bool is_three_of_a_kind(const std::vector<int>& input)
{
    int same_cnt = 1;
    int three_cnt = 0;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_value(input[idx-1]) == get_value(input[idx]))
            ++same_cnt;
        else
        {
            if (same_cnt == 3)
                three_cnt += 1;
            same_cnt = 1;
        }
    }
    if (same_cnt == 3)
        three_cnt += 1;
    return three_cnt == 1;
}

// @breif 两队
bool is_two_pair(const std::vector<int>& input)
{
    int same_cnt = 1;
    int two_cnt = 0;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_value(input[idx-1]) == get_value(input[idx]))
            ++same_cnt;
        else
        {
            if (same_cnt == 2)
                two_cnt += 1;
            same_cnt = 1;
        }
    }
    if (same_cnt == 2)
        two_cnt += 1;
    return two_cnt == 2;
}

// @brief 一对
bool is_one_pair(const std::vector<int>& input)
{
    int two_cnt = 0;
    int same_cnt = 1;
    for (std::size_t idx = 1; idx < input.size(); ++idx)
    {
        if (get_value(input[idx-1]) == get_value(input[idx]))
            ++same_cnt;
        else
        {
            if (same_cnt == 2)
                two_cnt += 1;
            same_cnt = 1;
        }
    }
    if (same_cnt == 2)
        two_cnt += 1;
    return two_cnt == 1;
}

//    kHighCard = 10,  // 高牌

// @brief 获取手牌牌型
// @params
//   @input: 排序好的手牌，数量==5
int get_hand_card_type(const std::vector<int>& input)
{
    if (input.size() != 5)
        return EPokerHandType::kInvalid;
    std::vector<int> hand_cards(input);
    std::sort(hand_cards.begin(), hand_cards.end(), compare_card_lesser);
    if (is_royal_flush(hand_cards))
        return EPokerHandType::kRoyalFlush;
    else if (is_straigth_flush(hand_cards))
        return EPokerHandType::kStraigthFlush;
    else if (is_four_of_a_kind(hand_cards))
        return EPokerHandType::kFourOfAKind;
    else if (is_full_house(hand_cards))
        return EPokerHandType::kFullHouse;
    else if (is_flush(hand_cards))
        return EPokerHandType::kFlush;
    else if (is_straight(hand_cards))
        return EPokerHandType::kStraigth;
    else if (is_three_of_a_kind(hand_cards))
        return EPokerHandType::kThreeOfAKind;
    else if (is_two_pair(hand_cards))
        return EPokerHandType::kTwoPair;
    else if (is_one_pair(hand_cards))
        return EPokerHandType::kOnePair;
    else
        return EPokerHandType::kHighCard;
    return EPokerHandType::kInvalid;
}

bool testcase1_1()
{
    std::vector<int> hand_card_1{0x10a, 0x10b, 0x10c, 0x10d, 0x10e};
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type(hand_card_1));

    std::vector<int> hand_card_2{0x104, 0x105, 0x106, 0x107, 0x108};
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type(hand_card_2));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type(std::vector<int>{0x109,0x10a,0x10b,0x10c,0x10d}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type(std::vector<int>{0x10e,0x102,0x103,0x104,0x105}));

    std::vector<int> hand_card_3{0x103, 0x203, 0x303, 0x403, 0x207};
    assert(EPokerHandType::kFourOfAKind == get_hand_card_type(hand_card_3));
    std::vector<int> hand_card_4{0x105, 0x205, 0x305, 0x409, 0x209};
    assert(EPokerHandType::kFullHouse == get_hand_card_type(hand_card_4));
    std::vector<int> hand_card_5{0x108, 0x208, 0x308, 0x102, 0x303};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type(hand_card_5));
    std::vector<int> hand_card_6{0x105, 0x206, 0x307, 0x408, 0x309};
    assert(EPokerHandType::kStraigth == get_hand_card_type(hand_card_6));
    std::vector<int> hand_card_7{0x106, 0x206, 0x306, 0x408, 0x209};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type(hand_card_7));
    std::vector<int> hand_card_8{0x106, 0x206, 0x308, 0x408, 0x209};
    assert(EPokerHandType::kTwoPair == get_hand_card_type(hand_card_8));
    std::vector<int> hand_card_9{0x103, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kOnePair == get_hand_card_type(hand_card_9));
    std::vector<int> hand_card_10{0x10a, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kHighCard == get_hand_card_type(hand_card_10));

    return true;
}

bool testcase2_1()
{
    std::vector<int> hand_card_1{0x10a, 0x10b, 0x10c, 0x10d, 0x10e};
    assert(EPokerHandType::kRoyalFlush == get_hand_card_type_v2(hand_card_1));

    std::vector<int> hand_card_2{0x104, 0x105, 0x106, 0x107, 0x108};
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(hand_card_2));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x109,0x10a,0x10b,0x10c,0x10d}));
    assert(EPokerHandType::kStraigthFlush == get_hand_card_type_v2(std::vector<int>{0x10e,0x102,0x103,0x104,0x105}));

    std::vector<int> hand_card_3{0x103, 0x203, 0x303, 0x403, 0x207};
    assert(EPokerHandType::kFourOfAKind == get_hand_card_type_v2(hand_card_3));
    std::vector<int> hand_card_4{0x105, 0x205, 0x305, 0x409, 0x209};
    assert(EPokerHandType::kFullHouse == get_hand_card_type_v2(hand_card_4));
    std::vector<int> hand_card_5{0x108, 0x208, 0x308, 0x102, 0x303};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_5));
    std::vector<int> hand_card_6{0x105, 0x206, 0x307, 0x408, 0x309};
    assert(EPokerHandType::kStraigth == get_hand_card_type_v2(hand_card_6));
    std::vector<int> hand_card_7{0x106, 0x206, 0x306, 0x408, 0x209};
    assert(EPokerHandType::kThreeOfAKind == get_hand_card_type_v2(hand_card_7));
    std::vector<int> hand_card_8{0x106, 0x206, 0x308, 0x408, 0x209};
    assert(EPokerHandType::kTwoPair == get_hand_card_type_v2(hand_card_8));
    std::vector<int> hand_card_9{0x103, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kOnePair == get_hand_card_type_v2(hand_card_9));
    std::vector<int> hand_card_10{0x10a, 0x203, 0x204, 0x205, 0x206};
    assert(EPokerHandType::kHighCard == get_hand_card_type_v2(hand_card_10));

    return true;
}

// @brief 随机从src中获取cnt个元素放到dst中
void random_select_n(std::vector<int>& dst, int cnt, std::vector<int>& src)
{
    if (cnt <= 0)
        return;
    srand(time(nullptr));
    int src_cnt = src.size();
    // 源数据不够
    if (src_cnt < cnt)
        return;
    int selected_cnt = 0;
    while(selected_cnt < cnt)
    {
        int target = rand() % (src_cnt - selected_cnt);
        dst.push_back(src[target]);
        selected_cnt++;
        std::swap(src[target], src[cnt - selected_cnt]);
    }
}

// @brief 随机获取五张牌，检查两个计算函数结果是否一致
bool testcase3_1()
{
    int cnt = 500000;
    std::vector<int> all_cards(ALL_CARDS, ALL_CARDS + (sizeof(ALL_CARDS)/sizeof(ALL_CARDS[0])));
    std::mt19937 gen(static_cast<unsigned int>(time(0)));
    std::shuffle(all_cards.begin(), all_cards.end(), gen);
    uint64_t v1_cost_ns = 0;
    uint64_t v2_cost_ns = 0;
    while(--cnt > 0)
    {
        std::vector<int> hand_cards;
        random_select_n(hand_cards, 5, all_cards);
        auto time_point = std::chrono::high_resolution_clock::now();
        int result_v1 = get_hand_card_type(hand_cards);
        auto end_time_point = std::chrono::high_resolution_clock::now();
        auto duration = end_time_point - time_point;
        v1_cost_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();

        time_point = std::chrono::high_resolution_clock::now();
        int result_v2 = get_hand_card_type_v2(hand_cards);
        end_time_point = std::chrono::high_resolution_clock::now();
        duration = end_time_point - time_point;
        v2_cost_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
        if (result_v1 != result_v2)
        {
            std::cout << "invliad check: " << std::endl;
            for (auto card : hand_cards)
            {
                std::cout << std::hex << card << ",";
            }
            std::cout << std::endl;
            assert(false);
        }
    }
    std::cout << "show cost:" << std::endl;
    std::cout << "v1=" << v1_cost_ns << "ns" << std::endl;
    std::cout << "v2=" << v2_cost_ns << "ns" << std::endl;
    std::cout << "v2/v1=" << double(v2_cost_ns) / double(v1_cost_ns) << std::endl;
    return true;
}

int main()
{
    testcase1_1();
    testcase2_1();
    testcase3_1();
    return 0;
}